﻿""" asm.py
    LEE SangYoung
"""

import types, re
from wbox.gx import opcode

from opparser import parseop, parseops


class Instruction(object):
    def __init__(self, code):
        self.code = code
        
    def pack(self):
        data = "".join(map(chr, self.code))
        size = chr(len(data) + 2)
        return size + data + size

        
def parseinst():
    return Instruction(opcode.getcode(inst))
    

def Contact(object):
    pass
    
    
def LoadContact(Contact):
    pass
    
    
def CompareContact(Contact):
    pass
    
    
def OrContact(Contact):
    pass
    
    
def AndContact(Contact):
    pass

    
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


class Assembler(object):
    p_re = re.compile(r"[pP][0-9]+")
    
    def parse(self, src):
        if type(src) != types.ListType:
            src = src.splitlines()
    
        L = []
        for line in src:
            result = self.parseline(line)
            if result:
                L.append(result)
        return L
        
    def parseline(self, line):
        if not line:
            return None
            
        if line[0] == ";":
            return self.statement(line)
        else:
            return self.instruction(line)
            
    def statement(self, line):
        line = line[1:].encode("euc-kr")
        if len(line) > 0 and line[0] == "*":
            data = "\x80\x01" + line[1:]
        else:
            data = "\x80" + chr(2 + (len(line) + 1) // 2) + line
        n = chr(len(data) + 2)
        return n + data + n
        
    def is_dword(self, inst):
        return inst[0:1] == "D" or inst[0:3] == "LDD"
        
    def instruction(self, line):
        items = line.split()
        if len(items) < 1:
            return
        inst = items[0].upper()
        try:
            args = parseops(" ".join(items[1:]))
        except:
            print line
            raise
        
        _inst = inst
        if hasattr(self, inst):
            func = getattr(self, inst)
            inst = func(inst, args)
        else:
            if inst.find(".") != -1:
                inst = self.extcommand(inst, args)
            else:
                if self.p_re.match(inst):
                    args = [parseop(inst)]
                    inst = "SUB"
                inst = self.default(inst, args)
        
        try:
            L = [Instruction(inst).pack()]
            dword = self.is_dword(_inst) 
            for arg in args:
                L.append(arg.pack(dword))
        except:
            print line
            print inst, args
            raise
        
        return L
        
    def addition_step(self, inst, args):
        code = opcode.getcode(inst, len(args))
        step = 0
        dword = self.is_dword(inst)
        for arg in args:
            for item in arg.items:
                if dword and (item.code in [0xe8, 0xea]): # 정수를 포함하는 DWORD 명령
                    step += 1
                if item.has_sep():
                    if inst == "FMOV":
                        step += 1
                    else:
                        step += 2
        return (code[0], code[1] + step) + code[2:]
        
    def default(self, inst, args):
        try:
            if len(args) > 1:
                return self.addition_step(inst, args)
            return opcode.getcode(inst)
        except KeyError:
            return None
            
    def extcommand(self, inst, args):
        ext, cmd = inst.split(".")
        try:
            code = opcode.getcode(ext + ".")
            arg_step = len(args) - 1
            cmd_step = 3 + (len(cmd) + 1) // 2
            stepno = cmd_step + arg_step
            return (code[0], stepno, arg_step, code[3]) + tuple(map(ord, cmd))
        except:
            return None
        
    def OUT(self, inst, args):
        if args[0].mnemonic == "T":
            return (0x21, 0x04)
        return (0x20,)
        
    def OUTH(self, inst, args):
        if args[0].mnemonic != "T":
            raise Exception("only T")
        return (0x22, 0x04)
        
    def CALL(self, inst, args):
        argc = len(args)
        steps = argc + 1
        subcode = 0 + argc
        return 0x54, steps, subcode
    
    
if __name__ == '__main__':
    src = """\
DMOV K0 D0
LDD> K0 D0
"""
    asm = Assembler()
    print asm.parse(src)
